package com.dces.jx.billtest.dataimport.base;

import com.dces.demo.dataimport.base.AbstractTempTable;
import com.dces.jx.billtest.config.BulkInsertConfig;
import com.xdo.bulkinsert.BulkInsertException;
import com.xdo.bulkinsert.BulkInsertFactory;
import com.xdo.dataimport.base.ImportHandlerInterface;
import com.xdo.dataimport.model.ExcelImportDto;
import com.xdo.dataimport.utils.XdoImportLogger;
import com.xdo.domain.ResultObject;
import com.xdo.i.IBulkInsert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

import static com.dces.jx.billtest.dataimport.constant.Constants.CORRECT_FLAG;


/**
 * @author zyzhu
 * @date 2021/08/31
 *
 * 抽象-导入数据执行器(临时表校验导入使用)
 * 支持新增导入，修改导入，删除导入
 */
@Component
@Slf4j
public abstract class AbstractTempTabImportHandler<Temp extends AbstractTempTable> implements ImportHandlerInterface {

    @Resource
    private BulkInsertConfig bulkInsertConfig;

    /**
     * 业务校验方法（内存校验）
     *
     * @param mapBasicParam    任务基础参数(当前任务的sid、taskCode、tradeCode、importType、insertUserOid、insertUserName)
     * @param mapBusinessParam 业务自定义参数(比如insertUser、插入表体时用到的表头id等都可以在这里传入,需在前端导入时确定需要用到的参数并传递)
     * @param mapObjectList    实体列表map，可根据xmlContent的sheet的id获取某类实体列表
     * @return 业务校验结果，head根据xmlContent的sheet的field获取，无需设置；当采用临时表校验方式时tempOwnerId必须设置，否则插入正确数据时无法进行操作
     */
    @Override
    public List<ExcelImportDto> checkData(Map<String, Object> mapBasicParam, Map<String, Object> mapBusinessParam, Map<String, List<Object>> mapObjectList) throws Exception {
        List<ExcelImportDto> excelImportDtoList = new ArrayList<>();
        // 根据配置文件的每个sheet的id获取特定sheet转化出来的实体列表，按配置顺序获取
        String sheetId = getSheetId();
        if (!mapObjectList.containsKey(sheetId)) {
            XdoImportLogger.log("导入数据序列化的实体列表集合中不存在" + sheetId);
            return null;
        }
        if (checkBusinessParam(mapBusinessParam)) {
            List<Object> objectList = mapObjectList.get(sheetId);
            int intObjectListSize = objectList.size();
            List<Temp> beanDataList = Collections.synchronizedList(new ArrayList<>(intObjectListSize));
            String tempOwnerId = UUID.randomUUID().toString();
            setTempDataList(tempOwnerId, objectList, beanDataList);
            int intEffectCount = 0;
            try {
                if (CollectionUtils.isNotEmpty(beanDataList)) {
                    // 初始化快速入库实例（在config文件夹下新建OracleBulkConfig用于读取配置文件中的数据库连接配置）
                    IBulkInsert iBulkInsert = BulkInsertFactory.createInstance(bulkInsertConfig);
                    intEffectCount = iBulkInsert.fastImport(beanDataList);
                }
            } catch (BulkInsertException ex) {
                intEffectCount = ex.getCommitRowCount();
                XdoImportLogger.log("执行数据批量入库时error,临时表已插入%d条", intEffectCount);
                XdoImportLogger.log(ex.getMessage());
                throw ex;
            } catch (Exception ex) {
                XdoImportLogger.log("执行数据批量入库时error,临时表已插入%d条", intEffectCount);
                XdoImportLogger.log(ex.getMessage());
                throw ex;
            }
            XdoImportLogger.log("执行数据批量入库,临时表已插入%d条", intEffectCount);
            // 初始化正确数据集合
            List<Temp> correctList = Collections.synchronizedList(new ArrayList<>());
            // 初始化错误数据集合
            List<Temp> wrongList = Collections.synchronizedList(new ArrayList<>());
            // 初始化警告数据集合
            List<Temp> warnList = Collections.synchronizedList(new ArrayList<>());
            checkTempData(correctList, wrongList, warnList, mapBasicParam, mapBusinessParam, tempOwnerId);
            ExcelImportDto<Temp> excelImportDto = new ExcelImportDto<>();
            XdoImportLogger.log("correct：%d;wrong：%d;warn:%d", correctList.size(), wrongList.size(), warnList.size());
            excelImportDto.setCorrectList(correctList);
            excelImportDto.setWrongList(wrongList);
            excelImportDto.setWarnList(warnList);
            excelImportDto.setTempOwnerId(tempOwnerId);
            excelImportDtoList.add(excelImportDto);
        } else {
            XdoImportLogger.log("部分自定义参数未成功获取(emsNo, cavMark)");
            return null;
        }
        return excelImportDtoList;
    }

    /**
     * 封装临时表栏位值(业务实现类可视情况自己改写此方法)
     * @param tempOwnerId
     * @param objectList
     * @param beanDataList
     */
     protected void setTempDataList(String tempOwnerId, List<Object> objectList, List<Temp> beanDataList) {
        for (int i = 0;i < objectList.size();i ++ ) {
            Temp temp = (Temp) objectList.get(i);
            temp.setSid(UUID.randomUUID().toString());
            temp.setTempOwner(tempOwnerId);
            temp.setTempFlag(CORRECT_FLAG);
            beanDataList.add(temp);
        }
    }

    /**
     * 获取导入的页面id
     *
     * @return 返回excel中sheet名称
     */
    protected String getSheetId() {
        return getTaskCode();
    }

    /**
     * 每批次数据校验总方法
     *
     * @param correctList 正确数据集合
     * @param wrongList 错误数据集合
     * @param warnList 警告数据集合
     * @param mapBasicParam 基础业务参数
     * @param mapBusinessParam 账册编号等业务参数
     * @param tempOwnerId 导入批次号
     * @return 校验成功true,失败false
     */
    protected abstract void checkTempData(List<Temp> correctList, List<Temp> wrongList, List<Temp> warnList,
                                             Map<String, Object> mapBasicParam, Map<String, Object> mapBusinessParam,
                                             String tempOwnerId);

    /**
     * 业务持久化数据方法
     *
     * @param mapBasicParam    任务基础参数(当前任务的sid、taskCode、tradeCode、importType、insertUserOid、insertUserName)
     * @param mapBusinessParam 业务自定义参数(比如insertUser、插入表体时用到的表头id等都可以在这里传入,需在前端导入时确定需要用到的参数并传递)
     * @param mapObjectList    正确数据序列化的实体列表集合，根据导入配置文件(xmlContent)的每个sheet配置的id获取相应实体列表（采用内存校验时传递）
     * @param tempOwnerIdList  临时表处理批次Id，单sheet时取第一条（采用临时表校验时传递）
     * @return 业务持久化数据结果，导入服务根据返回值设置任务状态
     */
    @Override
    public ResultObject persistData(Map<String, Object> mapBasicParam, Map<String, Object> mapBusinessParam,
                                    Map<String, List<Object>> mapObjectList, List<String> tempOwnerIdList) throws Exception {
        ResultObject resultObject = ResultObject.createInstance(true, "导入成功");
        // 从自定义参数中获取需要的数据，此处是导入表体，需获取对应表头的id和当前操作者
        if (!checkBusinessParam(mapBusinessParam)) {
            resultObject.setSuccess(false);
            resultObject.setMessage("部分自定义参数未成功获取");
            return resultObject;
        }
        //遍历当前实体列表进行转换插入
        String strErr = "";
        if (CollectionUtils.isNotEmpty(tempOwnerIdList)) {
            insertOrUpdateTempData(tempOwnerIdList.get(0), mapBasicParam, mapBusinessParam);
            XdoImportLogger.log("该批次导入数据成功");
        } else {
            strErr = "未获取本次导入批次号！";
        }
        if (StringUtils.isNotBlank(strErr)) {
            resultObject.setSuccess(false);
            resultObject.setMessage(strErr);
        }
        return resultObject;
    }

    // -------------------以下方法均为业务校验方法-------------------
    /**
     * 校验: 业务参数是否传入
     *
     * @param mapBusinessParam 业务参数
     * @return 正确反悔true，错误返回false
     */
    private boolean checkBusinessParam(Map<String, Object> mapBusinessParam) {
        return MapUtils.isNotEmpty(mapBusinessParam);
    }

    /**
     * 更新数据方法
     * @param tempOwnerId 批次号
     * @param mapBasicParam 基础参数
     * @param mapBusinessParam 业务参数
     */
    protected abstract void insertOrUpdateTempData(String tempOwnerId, Map<String, Object> mapBasicParam,
                                                  Map<String, Object> mapBusinessParam);

}
